/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package yoga.algo.process;

import java.util.ArrayList;
import yoga.algo.coding.ICoding;
import yoga.algo.phenotype.IFitnessCalculator;
import yoga.algo.phenotype.Individual;
import yoga.algo.phenotype.Population;
import yoga.algo.phenotype.PopulationStatistics;

/**
 *
 * @author Yogev Montekyo
 */
public class MatingScheme<T>
{
    private ISelectionMethod<T> m_selectionMethod;
    private IReproducionMethod<T> m_reproductionMethod;
    private IFitnessCalculator<T> m_fitnessCalculator;
    private String m_schemeDescription;
    private final double MutationProbablity = 0.2;
    private final double ElitismPercent = 0.1;

    public MatingScheme(String schemeDescription, ISelectionMethod<T> selectionMethod, IFitnessCalculator<T> fitnessCalculator, IReproducionMethod<T> reproductionMethod)
    {
        m_selectionMethod = selectionMethod;
        m_fitnessCalculator = fitnessCalculator;
        m_reproductionMethod = reproductionMethod;
        m_schemeDescription = schemeDescription;
    }

    public IFitnessCalculator<T> getFitnessCalculator()
    {
        return m_fitnessCalculator;
    }

    public double getMutationProbability()
    {
        return MutationProbablity;
    }

    public Population<T> executeSchemeAndGetNextGeneration(Population<T> currentPopulation)
    {
        PopulationStatistics<T> populationStatistics = new PopulationStatistics<T>(m_fitnessCalculator);
        populationStatistics.updateStatistics(currentPopulation);

        Population<T> populationNextGeneration = new Population<T>();

        //
        while (populationNextGeneration.size() < currentPopulation.size() * ElitismPercent)
        {
            Individual<T> elitist = m_selectionMethod.getSelectedIndividual(populationStatistics);
            populationNextGeneration.add(elitist);            
        }
        
        while (populationNextGeneration.size() < currentPopulation.size())
        {
            Individual<T> parent1 = m_selectionMethod.getSelectedIndividual(populationStatistics);
            //parent1.getCoding().mutate(ICoding.MutationTypes.Single, m_mutationProbablity);

            Individual<T> parent2 = m_selectionMethod.getSelectedIndividual(populationStatistics);
            //parent2.getCoding().mutate(ICoding.MutationTypes.Single, m_mutationProbablity);

            Individual<T> child1 = m_reproductionMethod.reproduceChild(parent1, parent2);
            child1.getCoding().mutate(ICoding.MutationTypes.Single, MutationProbablity);
            populationNextGeneration.add(child1);

            Individual<T> child2 = m_reproductionMethod.reproduceChild(parent1, parent2);
            child2.getCoding().mutate(ICoding.MutationTypes.Single, MutationProbablity);
            populationNextGeneration.add(child2);
        }

        return populationNextGeneration;
    }

    public String getDescription()
    {
        return m_schemeDescription;
    }

    public ArrayList<Individual<T>> getNFittestMates(Population<T> population, int numberOfMates)
    {
        PopulationStatistics<T> populationStatistics = new PopulationStatistics<T>(m_fitnessCalculator);
        populationStatistics.updateStatistics(population);
        ArrayList<Individual<T>> listOfMates = new ArrayList<Individual<T>>();

        while (numberOfMates > 0)
        {
            listOfMates.add(m_selectionMethod.getSelectedIndividual(populationStatistics));
            numberOfMates--;
        }

        return listOfMates;
    }

    public Individual<T> reproduceChild(Individual<T> parnet1, Individual<T> parent2)
    {
        return m_reproductionMethod.reproduceChild(parnet1, parent2);
    }
}
